﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using Samurai.WorkflowGraph.EventArgs;
using Samurai.WorkflowGraph.Models;
using Telerik.Windows.Controls;

namespace Samurai.WorkflowGraph.ViewModels
{
    public class WorkflowGraphViewModel : ViewModelBase
    {
        private bool _isBusy;
        private string _busyContent = "preparing diagram";

        /// <summary>
        /// Initializes a new instance of the <see cref="WorkflowGraphViewModel"/> class.
        /// </summary>
        public WorkflowGraphViewModel(IEnumerable<WorkflowStepNode> nodes)
        {
            GraphSource = new GraphSource();
            HierarchicalDataSource = new ObservableCollection<WorkflowStepNode>();
            HierarchicalDataSource = new ObservableCollection<WorkflowStepNode>(nodes);
            GraphSource.PopulateGraphSource(HierarchicalDataSource[0]);
            WireUpNodePropertyChangedEvents(HierarchicalDataSource[0]);

            _isBusy = true;
        }

        /// <summary>
        /// Occurs when a node's visibility is changed.
        /// </summary>
        public event EventHandler<VisibilityChangedEventArgs> NodeVisibilityChanged;

        /// <summary>
        /// Occurs when display delay elapsed. This event (and the display timer) are just hacks to workaround the Telerik layout issue with the diagram. Come on, Telerik!
        /// </summary>
        public event EventHandler DisplayDelayElapsed;

        /// <summary>
        /// Gets or sets the toggle visibility command.
        /// </summary>
        /// <value>
        /// The toggle visibility command.
        /// </value>
        public ICommand ToggleVisibilityCommand
        {
            get { return new DelegateCommand(ExecuteToggleVisibilityCommand, CanExecuteToggleVisibilityCommand); }
        }

        /// <summary>
        /// Gets the hierarchical data source.
        /// </summary>
        public ObservableCollection<WorkflowStepNode> HierarchicalDataSource { get; private set; }

        /// <summary>
        /// Gets the graph source.
        /// </summary>
        public GraphSource GraphSource { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is busy.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is busy; otherwise, <c>false</c>.
        /// </value>
        public bool IsBusy
        {
            get { return _isBusy; }
            set 
            { 
                if(_isBusy == value)
                    return;

                _isBusy = value;
                base.OnPropertyChanged(() => IsBusy);
            }
        }

        /// <summary>
        /// Gets or sets the content of the busy.
        /// </summary>
        /// <value>
        /// The content of the busy.
        /// </value>
        public string BusyContent
        {
            get { return _busyContent; }
            set
            {
                if(_busyContent == value) return;

                _busyContent = value;
                OnPropertyChanged(() => BusyContent);
            }
        }

        protected void NodePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName != "Visibility") return;

            if (NodeVisibilityChanged != null)
                NodeVisibilityChanged(sender, new VisibilityChangedEventArgs(((WorkflowStepNode) sender).Visibility == Visibility.Visible));
        }

        private void WireUpNodePropertyChangedEvents(HierarchicalNodeViewModel node)
        {
            foreach (var n in node.Children)
            {
                n.PropertyChanged += NodePropertyChanged;

                if (n.HasChildren)
                    WireUpNodePropertyChangedEvents(n);
            }
        }

        private void ToggleChildrenVisibility(WorkflowStepNode node, bool areChildrenVisible)
        {
            foreach (WorkflowStepNode subNode in node.Children)
            {
                var visibility = areChildrenVisible ? Visibility.Visible : Visibility.Collapsed;
                subNode.Visibility = visibility;
                GraphSource.InternalLinks.Where(link => link.Source == node).ToList().ForEach(link => link.Visibility = visibility);

                if (subNode.AreChildrenCollapsed)
                {
                    continue;
                }

                ToggleChildrenVisibility(subNode, areChildrenVisible);
            }
        }

        private bool CanExecuteToggleVisibilityCommand(object obj)
        {
            return obj != null && ((WorkflowStepNode)obj).Children.Count > 0;
        }

        private void ExecuteToggleVisibilityCommand(object obj)
        {
            if (obj == null) throw new ArgumentNullException("obj");

            var node = (WorkflowStepNode)obj;
            var areChildrenCollapsed = node.AreChildrenCollapsed;

            ToggleChildrenVisibility(node, areChildrenCollapsed);
            node.AreChildrenCollapsed = !node.AreChildrenCollapsed;
        }
    }
}
